#include "xtype.h"
#include "xdata_structure.h"
#include "xassert.h"
#include "xsys_log.h"
#include <algorithm>
#include <stdarg.h>
#ifdef XGE_USE_WIDE_CHAR
#define _STR2FLOAT _wtof
#define _STRLEN  wcslen
#define _SPRINTF swprintf_s // we use the safe version since the original one may lead some problem  NOT CROSS PLATFORM
#define _VSPRINTF vswprintf
#else
#define _STR2FLOAT atof
#define _STRLEN strlen
#define _SPRINTF sprintf_s // we use the safe version since it may lead some problem
#define _VSPRINTF vsprintf_s
#endif


//$Revision: 2012/1/10
// 1. Change the internal implementation of Str2Float 
// instead of doing the copy of the target chunk of string into the buffer than do the string2float operation ,
// we change the original string(XString) to obtain much more performance .
// 2. Change the internal implementation of ParseAsFlow , reduce the pre-test before the switch , added another
// test into the Switch-Case to obtain much more performance .

namespace xge{
namespace {
	// A simple string buffer
	class StringBuffer {
	public:
		StringBuffer( const char_t* str, size_t str_size , size_t size ) {
			XASSERT_BINARY(LESS,INT,str_size,size);
			string_.reserve( size );
			string_.append(str,str_size);
		}

		const String& AppendString( const char_t* fmt , ... );

		const String& string() const {
			return string_;
		}

		void Rollback( size_t size ) {
			string_.resize(size);
		}

	private:
		char_t* string_as_array() {
			return string_.empty() ? NULL : &(*string_.begin());
		}
		// string 
		String string_;
	};

	const String& StringBuffer::AppendString( const char_t* fmt , ... ) {
		const size_t this_size = string_.size();
		const size_t buffer_size = string_.capacity() - this_size;
		// if the buffer size is 0 
		// which means no buffer in the internal string
		// we simply return the default string as well
		if( buffer_size <= 1 ) 
			return string_;

		va_list list;
		va_start(list,fmt);
		string_.resize( string_.capacity()-1 );
		int ret = _VSPRINTF(string_as_array()+this_size,buffer_size-1,fmt,list);
		// failed
		if( ret < 0 || ret == buffer_size ) {
			// roll back
			string_.resize(this_size);
		}
		//success
		string_.resize(ret+this_size);
		return string_;
	}

	float Str2Float( const String& str , size_t from , size_t end ) {
		// The most safe way to get the internal buffer of a string of std is
		// to use the &begin() , it is discussed in the Google Chrome Source Code
		XASSERT(from<end);
		float ret ;
    String& mutable_ref = const_cast<String&>(str);
    // We use the XString internal buffer as our buffer for the _STR2FLOT macro
    if( end == mutable_ref.size() ) {
      // since we do not know a standard std::string is ended with '/0' or not 
      // so we just do it by expanding the string to a another explicit zero-end
      mutable_ref.push_back(_xT('\0'));
      // string as array goes here
      char_t* internal_buffer_start = &(*mutable_ref.begin());
      ret = (float)_STR2FLOAT( internal_buffer_start+from );
      // now we rollback all the changes here
      mutable_ref.pop_back();
    } else {
      // end must: less than mutable_ref.size()
      char_t* internal_buffer_start = &(*mutable_ref.begin());
      // we store the char at the index of end 
      // and change this char as well
      char_t stack_store_char = mutable_ref.at(end);
      mutable_ref[end] = _xT('\0');
      ret = (float)_STR2FLOAT( internal_buffer_start+from );
      // rollback
      mutable_ref[end] = stack_store_char;
    }

		return ret;
	}

	size_t ParseAsFlow( const String& str , size_t stack_top_pos , Point* point ) {
		// Parsing the string
		enum {
			STATE_EXPECT_START ,
			STATE_EXPECT_DIGIT_START,
			STATE_EXPECT_DIGIT_END ,
			STATE_FINISH 
		};
		int cur_state = STATE_EXPECT_START ;
		int digit_start_pos ;
		int digit_end_pos   ;
		// record the parsing num
		// if this value is 0 or 1 , that means when exiting
		// there exit no value parse or only parse 1 
		// may be the string is like (2.3) 
		int parsing_num = 0 ;
		// make the operation atomic
		// first store the parsing value into the point buffer
		// if the parsing error occur , the input buffer will
		// be untouched , if the error does not occur,copy the 
		// buffer

		// Do not use point as buffer, since it will generate
		// a trivial constructor call which consumes time
		float x,y;

		// Here is something we don't know
		// the std::string we don't know that it is a '\0' ended string or
		// something else , so we will check the '\0' and same as the size
		// of the string .
		const size_t str_size = str.size();
		do {
			const char_t next = str[stack_top_pos];
			switch(next) {
				// meeting for the end of the string
			case _xT('\0'): stack_top_pos = 0 ; goto __finish;
				// meeting the empty space just skip it
			case _xT(' '): ++stack_top_pos ; break;
			case _xT('('): 
				if( cur_state == STATE_EXPECT_START ) {
					// the current state is matched
					++stack_top_pos;
					cur_state = STATE_EXPECT_DIGIT_START ;
					break;
				} else {
					// error 
					stack_top_pos = 0;
					goto __finish;
				}
			case _xT(')'):
				if( cur_state == STATE_EXPECT_DIGIT_END ) {
					// unwind the number 
					y=( Str2Float(str,digit_start_pos,digit_end_pos) );
					// here is ok 
					++stack_top_pos;
					cur_state = STATE_FINISH;
					// increase the parsing number 
					++parsing_num ; 
					goto __finish;
				} else {
					// error
					stack_top_pos = 0; 
					goto __finish;
				}
			case _xT(','):
				if( cur_state == STATE_EXPECT_DIGIT_END ) {
					// unwind the number
					x=( Str2Float(str,digit_start_pos,digit_end_pos) );
					++stack_top_pos;
					cur_state = STATE_EXPECT_DIGIT_START ;
					// increasing the parsing number
					++parsing_num;
					break;
				} else {
					stack_top_pos = 0;
					goto __finish;
				}
			default:
        if( isdigit(next) || next == _xT('.') ) {
          if( cur_state == STATE_EXPECT_DIGIT_START ) {
            cur_state = STATE_EXPECT_DIGIT_END;
            digit_end_pos = digit_start_pos = stack_top_pos ;
            ++stack_top_pos ;
            break;
          } else if( cur_state == STATE_EXPECT_DIGIT_END ) {
            ++digit_end_pos;
            ++stack_top_pos;
            break;
          } 
        } else {
          // error goes here
          stack_top_pos = 0; 
          goto __finish;
        }
			}
		}while( stack_top_pos != 0 && stack_top_pos < str_size );

		// finish everything here
__finish:
		// return the position value
		if( stack_top_pos == 0 ) {
			return 0 ;
		} else {
			if( cur_state != STATE_FINISH || parsing_num != 2 ) {
				// also error
				return 0;
			} else {
				// success here
				point->set_x(x);
				point->set_y(y);
				return stack_top_pos;
			}
		}
	}


	bool CheckStringEmpty( const String& str , size_t size ) {
		// This function is only used to check the string is valid or not
		// on debug mode 
		// It will check from the position on will only contain empty space until the 
		// end of the string 

		for( ; str[size] != _xT('\0') && size != str.size() ; ++size ) {
			if( str[size] != _xT(' ') ) {
				return false;
			}
		}
		return true;
	}

}

		bool Point::ToPoint(Point* point, const String& str ) {
			// Parsing a point 
			size_t pos = ParseAsFlow(str,0,point);
			if( pos == 0 
#ifdef _DEBUG
				|| !CheckStringEmpty(str,pos) 
#endif
				) {
				// Parsing error
				SYS_LOG(PANIC,true,_xT("Parsing string to point has encountered error : ")
					_xT("the string contains more information than needed!String:%s"),str.c_str());
				return false;
			}
			return true;
	}

	const String& Point::ToString(const Point& pt) {
		static const size_t kLength = 8;
		static StringBuffer kStringBuffer(_xT("[Point]:"),kLength,512);
		kStringBuffer.Rollback(kLength);
		return kStringBuffer.AppendString(_xT("(%.3f,%.3f)"),pt.x(),pt.y());
	}

	bool Rect::ToRect( Rect* rect , const String& str ) {
		size_t pos = 0 ;
		Point pt[2];
		size_t times = 0 ; 

		do {
			pos = ParseAsFlow(str,pos,&pt[times]);
			++times ;
		} while( pos!=0 && times <2 );

#ifdef _DEBUG 
		if( pos !=0 && !CheckStringEmpty(str,pos) ) {
			// error
			SYS_LOG(PANIC,true,_xT("Parsing string to rect has encountered error : ")
				_xT("the string contains more information than needed!String:%s"),str.c_str());
			return false;
		}
#endif // DEBUG

		if( pos != 0 ) {
			rect->set_left_up_point(pt[0]);
			rect->set_width(pt[1].x());
			rect->set_height(pt[1].y());
			return true;
		} else {
			// error
			SYS_LOG(PANIC,true,_xT("Parsing string to rect has encountered error.String:%s\n"),str.c_str());
			return false;
		}
	}

	const String& Rect::ToString(const Rect& rect) {
		static const int kLength = 7 ;
		static StringBuffer kStringBuffer(_xT("[Rect]:"),kLength,1024);
		kStringBuffer.Rollback(kLength);
		kStringBuffer.AppendString(_xT("(%.3f,%.3f) "),rect.left_up_point().x(),rect.left_up_point().y());
		return kStringBuffer.AppendString(_xT("(%.3f,%.3f)."),rect.width(),rect.height());
	}



}